#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "zsocket.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#define TPORT 40001
#define TBACKLOG 20
#define MAX_EVENTS 10

int main()
{
    int listenfd = 0;
    int listenfdopt = 0;
    int listenfdoptsetr = 0;
    /* netinet/in.h */
    struct sockaddr_in localaddr;
    struct sockaddr_in cliaddr;
    int bindr = 0;
    int listenr = 0;
    int epfd = 0;
    struct epoll_event ev;
    struct epoll_event evs[MAX_EVENTS];
    int epollwaitr = 0;
    int i = 0;
    int fd = 0;
    int clifd = 0;
    //int cliaddrlen = 0;
    socklen_t cliaddrlen = {0};
    int n = 0;
    int nread = 0;
    char buf[BUFSIZ] = {0};

    //1. 创建listenfd
    listenfd = socket(AF_INET, SOCK_STREAM, ZDEFAULT_PROTOCOL);
    if(listenfd == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }

    //2. set listenfd nonblock
    /*listenfdopt = fcntl(listenfd, F_GETFL);
    if(listenfdopt == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }
    listenfdopt |= O_NONBLOCK;
    listenfdoptsetr = fcntl(listenfd, F_SETFL, listenfdopt);
    if(listenfdoptsetr == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }*/
    listenfdoptsetr = zsocket_stasetnonblock(listenfd);
    if(listenfdoptsetr == -1)
    {
        fprintf(stderr, "zsocket_stasetnonblock is -1\n");
        return -1;
    }

    //3.bind
    bzero(&localaddr, sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    localaddr.sin_port = htons(TPORT);
    bindr = bind(listenfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
    if(bindr == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }

    //4.listen
    listenr = listen(listenfd, TBACKLOG);
    if(listenr == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }

    //5.accept? no epoll_create!
    //since linux 2.6.8 the parameter of epoll_create is ignored, but must be greater than 0
    epfd = epoll_create(MAX_EVENTS);
    if(epfd == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }

    //6. epoll_ctl epfd - listenfd
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1)
    {
        fprintf(stderr, "err: %s\n", strerror(errno));
        return -1;
    }

    while(1)
    {
        printf("line 105");
        // -1 will block
        epollwaitr = epoll_wait(epfd, evs, MAX_EVENTS, -1);
        if(epollwaitr == -1)
        {
            fprintf(stderr, "err: %s\n", strerror(errno));
            return -1;
        }

        // foreach fds
        for(i = 0; i < epollwaitr; i++)
        {
            fd = evs[i].data.fd;
            if(fd == listenfd)
            {
                // 这个是监听的fd
                while((clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen)) > 0)
                {
                    zsocket_stasetnonblock(clifd);
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = clifd;
                    if(epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev) == -1)
                    {
                        fprintf(stderr, "err: ctl err!\n");
                        return -1;
                    }
                }
                if(clifd == -1)
                {
                    if(errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
                    {
                        fprintf(stderr, "line 132");
                    }
                }
                continue;
            }
            if(evs[i].events & EPOLLIN)
            {
                n = 0;
                while ((nread = read(fd, buf + n, BUFSIZ - 1)) > 0)
                {
                    n += nread;
                }

                if (nread == -1 && errno != EAGAIN)
                {
                    fprintf(stderr, "read err!\n");
                }
                ev.data.fd = fd;
                ev.events = evs[i].events | EPOLLOUT;
                if(epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1)
                {
                    fprintf(stderr, "epoll_ctl err\n");
                }
                printf("1\n");
            }
            if(evs[i].events & EPOLLOUT)
            {
                sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11);
                int nwrite, data_size = strlen(buf);
                n = data_size;
                while(n > 0)
                {
                    nwrite = write(fd, buf + data_size - n , n);
                    if (nwrite < n)
                    {
                        if(nwrite == -1 && errno != EAGAIN)
                        {
                            fprintf(stderr, "write err\n");
                        }
                        break;

                    }
                    n -= nwrite;
                }
                close(fd);
            }
        }
    }

    printf("OK\n");
    getchar();
    return 0;
}
